export function supportWebGL() {
  try {
    if (!window.WebGLRenderingContext) {
      return false;
    }

    var canvas = document.createElement('canvas');
    return !!(
      canvas.getContext('webgl') || canvas.getContext('experimental-webgl')
    );
  } catch (err) {
    return false;
  }
}

export default class Player {
  static logPrefix = '[jsmpeg-player] ';

  static WORKER_PATH = './jsmpeg.worker.min.js';
  /** @type {Worker[]} */
  static workers = [];
  /**
   * 当前所有激活的Worker
   */
  static allWorkers = [];
  static MAX_WORKER_SIZE = 1;
  static SUPPORT_WEBGL = supportWebGL();

  static createWorker() {
    const wk = new Worker(Player.WORKER_PATH, {});
    wk.startTime = performance.now();
    wk.waitingList = [];
    wk.ready = false;

    wk.addEventListener('message', evt => {
      const data = evt.data;
      console.log(Player.logPrefix + 'get message from worker', data);

      switch (data.type) {
        // 就绪
        case 'ready':
          console.log('worker 就绪，耗时', performance.now() - wk.startTime);
          wk.ready = true;
          if (wk.waitingList.length) {
            const list = wk.waitingList;
            wk.waitingList = [];
            for (const i of list) {
              i();
            }
          }
          break;

        case 'destroyed':
          // 已销毁， 放回线程池
          console.log('worker 已清理');

          break;

        case 'fatal':
          // 严重错误
          if (wk.player) {
            wk.player.onFatal(data.data);
          }
          wk.terminate();
          break;

        case 'created':
          if (wk.player) {
            wk.player.onCreated();
          }
          break;

        case 'initial':
          break;

        case 'restart':
          // 需要重启
          if (wk.player) {
            wk.player.onRestart()
          }
          break
      }
    });

    wk.addEventListener('error', err => {
      console.log(Player.logPrefix + 'get error from worker()', err);
      if (wk.player) {
        wk.player.onError(err);
      }
    });

    // 记录当前所有 worker
    this.allWorkers.push(wk);

    const _t = wk.terminate;
    wk.terminate = () => {
      const idx = this.allWorkers.indexOf(wk);
      if (idx !== -1) {
        this.allWorkers.splice(idx, 1);
      }

      _t.call(wk);
    };

    return wk;
  }

  /**
   * 获取线程池 worker, 可以携带url，方便找到之前worker的复用
   * @param {string} prefer
   */
  static chooseWorker(prefer) {
    if (!this.workers.length) {
      return null;
    }

    if (this.workers.length === 1) {
      return this.workers.pop();
    }

    const pick = this.workers.findIndex(i => i.id === prefer);
    if (pick !== -1) {
      const wk = this.workers[pick];
      this.workers.splice(pick, 1);
      return wk;
    }

    return this.workers.pop();
  }

  /**
   * 判断是否存在 指定 id 的worker
   * @param {string} id
   */
  static hasWorker(id) {
    if (this.allWorkers.length) {
      return this.allWorkers.find(i => i.id === id);
    }

    return false;
  }

  /**
   * 判断是否存在 指定 id 的空闲 worker
   * @param {string} id
   */
  static hasIdleWorker(id) {
    if (this.workers.length) {
      return this.workers.find(i => i.id === id);
    }

    return false;
  }

  /**
   * 预热worker
   */
  static warnup() {
    Player.workers.push(this.createWorker());
  }

  /**
   * @param {{url: string, attachCanvas: (c: HTMLCanvasElement) => void, onError?: (error) => void, onCreated?: () => void  }} options
   */
  constructor(options = {}) {
    this.url = options.url;
    this.attachCanvas = options.attachCanvas
    this._onError = options.onError;
    this._onCreated = options.onCreated;
    this.initial();
  }

  resize() {
    if (this.created && this.wk) {
      this.wk.postMessage({
        type: 'resize',
      });
    }
  }

  async initial() {
    console.log(Player.logPrefix + '正在初始化播放器: ', this.url);

    if (Player.hasWorker(this.url) && !Player.hasIdleWorker(this.url)) {
      console.log(
        Player.logPrefix + '已存在相同 id 的非空闲 Worker, 等待一会再初始化: ',
        this.url,
      );
      await new Promise(r => setTimeout(r, 100));
    }

    let wk = Player.chooseWorker(this.url);
    if (wk) {
      console.log(Player.logPrefix + '从Worker池复用: ', this.url);
      this.wk = wk;
    } else {
      console.log(Player.logPrefix + '正在创建新的 Worker: ', this.url);
      this.wk = wk = Player.createWorker();
    }

    wk.player = this;
    if (wk.ready) {
      this.create();
    } else {
      wk.waitingList.push(this.create.bind(this));
    }
  }

  onError(err) {
    if (this._onError) {
      this._onError(err);
    }
  }

  onRestart() {
    this.create()
  }

  onCreated() {
    this.created = true;
    if (this._onCreated) {
      this._onCreated();
    }
  }

  onFatal(err) {
    if (!this.created) {
      console.log(Player.logPrefix + '复用worker失败，重新初始化', err);
      this.initial();
    } else if (!this.destroyed) {
      console.log(Player.logPrefix + 'fatal error, 准备重新创建播放器', err);
      this.initial();
    }
  }

  /**
   * 创建播放器
   */
  create() {
    console.log(Player.logPrefix + '正在创建 player: ' + this.url);
    // transferControlToOffscreen 只能创建一次
    const canvas = document.createElement('canvas')
    this.attachCanvas(canvas)
    const oc = canvas.transferControlToOffscreen();
    this.wk.id = this.url;
    this.wk.postMessage(
      {
        type: 'create',
        data: {
          canvas: oc,
          webgl: Player.SUPPORT_WEBGL,
          url: this.url,
        },
      },
      [oc],
    );
  }

  /**
   * 销毁播放器
   */
  destroy() {
    console.log(Player.logPrefix + '正在销毁 player: ' + this.url);
    this.wk.postMessage({
      type: 'destroy',
      data: {},
    });

    // 立即放入worker 池
    if (Player.workers.length < Player.MAX_WORKER_SIZE) {
      console.log(Player.logPrefix + '正在回收 Worker: ' + this.url);
      Player.workers.push(this.wk);
    } else {
      console.log(Player.logPrefix + '正在销毁 Worker: ' + this.url);
      this.wk.terminate();
    }

    this.destroyed = true;
  }
}
